# -*- coding: utf-8 -*-
import re
import json
from Common.mysql import redis_client
from Common.log import MyLog
from src.ApiTest.Core.base_request import Request
from src.ApiTest.Core.operational_sql import OperationalSql
from src.ApiTest.ProjectConfig.Database.baseInfo_database import BaseInfoConfig, BaseInfoConfigSchema
from src.ApiTest.ProjectConfig.Database.mysqlConfig_database import MysqlConfig, MysqlConfigSchema
from src.ApiTest.SqlData.Database.sql_database import SqlData, SqlSchema
from src.ApiTest.ConfigurationData.Database.data_database import TestData, TestDataSchema


class ApiRun:
    """
    API接口运行
    """

    def __init__(self):
        self.log = MyLog()

    def get_base_info(self, system_name, environment):
        """
        获取 base url 和公共请求头 header
        :param system_name:
        :param environment:
        :return:
        """
        base_info_data = BaseInfoConfig.query.filter_by(systemName=system_name).first()
        if base_info_data:
            base_schema = BaseInfoConfigSchema()
            base_info = base_schema.dump(base_info_data)
            # base URL
            if environment == 'test':
                base_url = base_info['base_url_test']
            elif environment == 'develop':
                base_url = base_info['base_url_develop']
            else:
                base_url = base_info['base_url_release']
            # 判断baseUrl是否配置
            if not base_url:
                raise KeyError(f'当前环境没有配置baseUrl信息，请检查系统配置是否配置正确')
            # 公共请求头headers
            base_headers = json.loads(base_info['header'])
        else:
            raise KeyError(f'当前环境没有配置系统基础信息，请检查系统配置是否配置正确')

        base_request_info = {
            "base_url": base_url,
            "base_headers": base_headers
        }
        return base_request_info

    def deep_traverse_and_replace(self, dic, target_value, new_value):
        """
        深度遍历嵌套字典和列表，并替换目标键的值。
        :param dic: 嵌套字典或列表
        :param target_value: 需要替换的目标值
        :param new_value: 新值
        """
        if isinstance(dic, dict):
            for key, value in dic.items():
                old_value = "${" + target_value + "}"
                if value == old_value:
                    dic[key] = new_value
                else:
                    self.deep_traverse_and_replace(value, target_value, new_value)
        elif isinstance(dic, list):
            for item in dic:
                self.deep_traverse_and_replace(item, target_value, new_value)

    def replace_parameter(self, pre_list, pre_parameter, environment, request_data, user_id):
        """
        替换请求信息中的行参
        :param pre_list: 请求信息中的行参
        :param pre_parameter: 接口信息中配置的前置参数
        :param environment: 运行环境
        :param request_data: 请求信息（请求头或请求体）
        :param user_id: 执行用例操作用户
        :return:
        """
        # TODO 需要判断前置参数的类型，比如：int，bool，list，dict等
        # 定义替换参数字典
        replace_parameter = {}
        for key in pre_list:
            found = False
            for data in pre_parameter:
                # 判断参数名是否相同
                if data['parameterName'][0] == key or data['parameterName'][0].split('/')[0] == key:
                    # 如果参数名相同，再判断参数类型
                    if data['parameterType'] == "系统配置参数":
                        # 处理替换 系统配置 类型的参数
                        try:
                            parameter_id = data['parameterName'][0].split('/')[1]
                            parameter_data = TestData.query.filter_by(id=parameter_id).first()
                            parameter_schema = TestDataSchema()
                            parameter_data = parameter_schema.dump(parameter_data)
                            replace_parameter[key] = parameter_data['data_value']
                            found = True
                            pre_parameter.remove(data)
                            break
                        except Exception as e:
                            raise e
                    elif data['parameterType'] == "接口返回参数":
                        parameter_value = redis_client.get('user_' + str(user_id) + ':' + data['parameterName'][0])
                        replace_parameter[key] = parameter_value
                        found = True
                        pre_parameter.remove(data)
                        break
                    elif data['parameterType'] == "SQL查询参数":
                        try:
                            sql_id = data['parameterName'][0].split('/')[1]
                            # SQL 参数名
                            sql_parameter_name = data['parameterName'][1]
                        except Exception as e:
                            return KeyError('sql配置参数错误' + str(e))
                        # 查询SQL基础信息
                        sql_data = SqlData.query.filter_by(id=sql_id).first()
                        sql_data_schema = SqlSchema()
                        sql_info = sql_data_schema.dump(sql_data)
                        func = sql_info['functionName']
                        if '[' in func:
                            func = func.split('[')[0]
                        sql = sql_info['sqlData']
                        # 查询当前环境的项目数据库基础信息（项目名称、环境、数据库类型）
                        db_type_mapping = {
                            'sql': 'mysql',
                            'redis': 'redis'
                        }
                        db_type = db_type_mapping.get('sql' if 'sql' in func else 'redis' if 'redis' in func else None)
                        project_id = sql_info['projectId']
                        db_data = MysqlConfig.query.filter_by(projectId=project_id, databaseType=db_type, environment=environment).first()
                        db_schema = MysqlConfigSchema()
                        db_info = db_schema.dump(db_data)
                        # 执行SQL
                        sql_result = OperationalSql().operational_sql(db_info, func, eval(sql))
                        # 替换参数
                        sql_value = sql_result[0][sql_parameter_name]
                        replace_parameter[key] = sql_value
                        found = True
                        pre_parameter.remove(data)
                        break
            if not found:
                return ValueError(f"没有匹配到参数: {key}")
        # 替换请求头中的前置参数
        for key in replace_parameter:
            self.deep_traverse_and_replace(request_data, key, replace_parameter[key])
        return request_data

    def api_request(self, environment, api_data, user_id):
        system_name = api_data['systemName']
        base_request_info = self.get_base_info(system_name, environment)

        # 重组请求URL
        path = api_data['path']
        request_url = base_request_info['base_url'] + path
        method = api_data['method']

        # 重组请求头
        base_headers = base_request_info['base_headers']
        headers = api_data['headers']
        if not base_headers:
            base_headers = '{}'
        if not headers:
            headers = '{}'
        try:
            request_headers = dict(base_headers, **eval(headers))
        except Exception as e:
            return KeyError('合并请求头参数错误,请检查请求头形参格式' + str(e))

        # 接口信息中配置的前置参数
        pre_parameter = json.loads(api_data['pre_parameter'])

        # 遍历请求头中的行参，输出参数列表
        headers_pre_list = re.findall(r"\$\{([^}]*)\}", str(request_headers))
        if headers_pre_list:
            # 替换请求头信息中的行参
            request_headers = self.replace_parameter(headers_pre_list, pre_parameter, environment, request_headers, user_id)

        # 遍历请求体中的行参，输出参数列表
        body = api_data['body']
        if body:
            body_pre_list = re.findall(r"\$\{([^}]*)\}", body)
            if body_pre_list:
                # 替换请求体信息中的行参
                body = self.replace_parameter(body_pre_list, pre_parameter, environment, eval(body), user_id)
            else:
                body = eval(body)

        files = ''
        params = ''

        # 发起接口请求
        response = Request().base_request(method, request_url, request_headers, body=json.dumps(body), files=files, params=params)
        # TODO pytest执行测试用例 直接传入接口执行结果response
        # 前端传入测试用例集信息，后端从测试用例集中取出测试用例，使用pytest框架执行用例，生成报告
        # pytest框架执行测试用例：取出的测试用例数据，传入test用例文件的方法中运行用例
        # test用例运行方法调用api_request进行接口请求
        res = response.json()
        # 提取后置参数
        after_parameter = json.loads(api_data['after_parameter'])
        for parameter in after_parameter:
            if parameter['dataName']:
                key_str = parameter['dataValue'].replace("['", ",").replace("']['", ",").replace("']", ",")
                key_list = key_str.split(',')
                aip_res = res
                for key in key_list:
                    if key:
                        try:
                            # 检查key是否为数字，如果是，则转换为int类型
                            if key.isdigit():
                                key = int(key)
                            aip_res = aip_res[key]
                        except Exception as e:
                            return KeyError('接口响应体中没有找到后置参数，请检查接口配置的后置参数' + str(e))
                # 将提取的参数写入redis
                redis_client.set('user_' + str(user_id) + ':' + parameter['dataName'], aip_res)
                redis_client.expire('user_' + str(user_id) + ':' + parameter['dataName'], 86400)

        return {'data': response.text, 'ErrorCode': 200}


